
---- keyboard shortcuts

-- switch between last used tabs
local last_buffer
events.connect(events.BUFFER_BEFORE_SWITCH, function()
	last_buffer = buffer
end)
keys['ctrl+\t'] = function() -- ctrl + tab
	if (last_buffer ~= nil) then
		view.goto_buffer(_VIEWS[1], last_buffer)
	end
end
keys['ctrl+pgup'] = function() -- ctrl + page down
	view.goto_buffer(_VIEWS[1], -1)
end
keys['ctrl+pgdn'] = function() -- ctrl + page down
	view.goto_buffer(_VIEWS[1], 1)
end
keys.cT = function() io.open_recent_file() end
keys['end'] = function() -- end - go to visible end of line (esp, when wrapped)
	buffer.line_end_display(buffer)
end
keys['home'] = function() -- home - go to visible beginning of line (esp, when wrapped)
	buffer.home_display(buffer)
end
keys['send'] = function() -- shift + end - select till the visible end of line (esp, when wrapped)
	buffer.line_end_display_extend(buffer)
end
keys['ctrl+p'] = function() -- ctrl + p
	textadept.menu.select_command()
end
keys['ctrl+g'] = textadept.editing.goto_line -- ctrl + g


---- theme
if not CURSES then
	view:set_theme("base16-custom-dark", {
		-- font = "Inconsolata", -- you can set it, if you want
		fontsize = 14
	})
end


---- convenient settings:
events.connect(events.BUFFER_NEW, function(str)
	-- use tabs, always
	buffer.use_tabs = true
	buffer.tab_width = 4
	-- show whitespace, always
	buffer.view_ws = buffer.WS_VISIBLEALWAYS
	-- wrap long lines, always
	buffer.wrap_mode = buffer.WRAP_WHITESPACE
	-- paste on multiple cursor places
	buffer.multi_paste = buffer.MULTIPASTE_EACH
end)

ui.silent_print = true

--[[
Braces like Atom.
Typing brace, e.g. { and pressing enter creates a new line with a tab between the brace.
]]
keys['\n'] = function() -- enter
	local caret_pos = buffer.current_pos
	local match = buffer:brace_match(caret_pos, 0)

	local current_line_number = buffer:line_from_position(caret_pos)
	local current_line = buffer.get_line(buffer, current_line_number)

	buffer.new_line(buffer) -- handle normal enter/return

	if string.match(current_line, "%s%*%s") ~= nil and string.match(current_line, "%s%*%s") ~= '' then
		buffer:add_text( '* ' )
	elseif string.match(current_line, "((.*)/%*%*)") ~= nil and string.match(current_line, "((.*)/%*%*)") ~= '' then
		-- typed /** and pressed enter, creates new line with *
		buffer:add_text( ' * ' )
	end

	if match ~= -1 then -- if braces (), [], {} are found then...
		buffer.goto_pos(buffer, caret_pos)
		buffer.new_line(buffer)
		buffer:add_text('\t')
	end

end


--[[
Javadoc/phpdoc implementation.
place cursor on previous line than function line,
then press ctrl+j
]]
keys['ctrl+j'] = function() -- ctrl + j
	local lexer_type = buffer:get_lexer(true)
	if lexer_type == 'php' then
		local current_line_number = buffer:line_from_position(buffer.current_pos)
		local function_line = buffer.get_line(buffer, current_line_number + 1)
		buffer.new_line(buffer)
		buffer.move_selected_lines_up(buffer)
		if string.find(function_line, "function") ~= nil then
			local comment = '/**\n * %1(description)\n *'
			local placeholder_num = 2
			for w in string.gmatch(function_line, "%$[%a_]+[%a%d_]*") do
				comment = comment..'\n * @param %'..placeholder_num..'(type) '..w..' %'..(placeholder_num + 1)..'(description)'
				placeholder_num = placeholder_num + 2
			end
			comment = comment..'\n * return %'..placeholder_num..'(type)\n */'
			textadept.snippets._insert(comment)
		end
	end
end

-- delete the line with line number
-- esp. for delete line
function delete_line(line_number)
	buffer.goto_line(buffer, line_number)
	buffer.line_end_extend(buffer)
	buffer.clear(buffer)
	buffer.clear(buffer) -- delete line ending
end

-- only keep unique items on the table
-- esp. for delete line
function unique_table(mytable)
	-- from: https://stackoverflow.com/a/20067270
	local hash = {}
	local res = {}

	for _,v in ipairs(mytable) do
		if (not hash[v]) then
			res[#res+1] = v
			hash[v] = true
		end
	end

	return res
end

-- for sorting a table
-- esp. for delete line
-- from: https://stackoverflow.com/a/15706820
function spairs(t, order)
	-- collect the keys
	local keys = {}
	for k in pairs(t) do keys[#keys+1] = k end

	-- if order function given, sort by it by passing the table and keys a, b,
	-- otherwise just sort the keys
	if order then
		table.sort(keys, function(a,b) return order(t, a, b) end)
	else
		table.sort(keys)
	end

	-- return the iterator function
	local i = 0
	return function()
		i = i + 1
		if keys[i] then
			return keys[i], t[keys[i]]
		end
	end
end

-- for getting line numbers that are selected or have a (multi)cursor on them
function get_selected_or_focus_lines()
	local lines = {}
	-- first we determine which lines we want to delete
	for i = 1, buffer.selections do
		local s, e = buffer.selection_n_start[i], buffer.selection_n_end[i]
		-- not a selection, just a cursor placed (supports multi cursor)
		if s == e then
			local line_num = buffer:line_from_position(s)
			table.insert(lines, line_num)
		-- selection (supports multi selection)
		else
			local line_num_start = buffer:line_from_position(s)
			local line_num_end   = buffer:line_from_position(e)

			for j = line_num_start, line_num_end do
				table.insert(lines, j)
			end
		end
	end

	-- we list one line only once
	-- this is a fix, if there are two selections in the same line
	lines = unique_table(lines)
	local kkey = 1
	local lines_ = {}
	for k,v in spairs(lines, function(t,a,b) return t[b] > t[a] end) do
		table.insert(lines_, v)
		kkey = kkey + 1
	end
	lines = lines_

	return lines
end

-- delete line(s)
-- supports multi selection, multi cursor or both
keys['ctrl+K'] = function() -- ctrl + shift + k
	buffer:begin_undo_action()

	local lines = get_selected_or_focus_lines()

	local lines_delete_offset = 0
	for key, line in ipairs(lines) do
		delete_line(line + lines_delete_offset)
		lines_delete_offset = lines_delete_offset - 1
	end
	buffer:end_undo_action()
end

-- pair multiline selections together in table
-- it will help us to process selections differently
function pair_line_numbers(lines)
	local paired_lines = {}
	local selection_open = false
	local last_begin = 0
	local last_end = 0

	for key, line in ipairs(lines) do
		if lines[key + 1] ~= nil and lines[key + 1] == lines[key] + 1 then
			if selection_open == false then
				last_begin = line
				selection_open = true
			end
		else
			if selection_open == true then
				last_end = line
				selection_open = false
				local data = {
					['beginning'] = last_begin,
					['ending'] = last_end
				}
				table.insert(paired_lines, data)
			else
				table.insert(paired_lines, line) -- normal line, not selection
			end
		end
	end

	return paired_lines
end

-- duplicate the lines passed
-- supports multi selections + multi cursors
function duplicate_line(line_number, offset)
	if (type(line_number) == "table") then
		local lines_queue = {}
		for i = line_number['beginning']+offset, line_number['ending']+offset do
			local line_contents = buffer.get_line(buffer, i)
			table.insert( lines_queue, line_contents )
		end

		buffer.goto_line(buffer, line_number['ending']+offset)
		for key, linetext in pairs(lines_queue) do
			buffer.line_end(buffer)
			buffer.new_line(buffer)

			local cursor_line = buffer:line_from_position( buffer.current_pos )
			textadept.editing.goto_line(cursor_line)
			buffer.line_end_display_extend(buffer)
			buffer.replace_sel(buffer, '')

			buffer.insert_text(buffer, -1, linetext)
			buffer.line_end(buffer)
			buffer.clear(buffer)
		end
	else
		buffer.goto_line(buffer, line_number + offset)
		buffer.line_duplicate()
	end
end

-- duplicate line(s)
-- supports multi selection, multi cursor or both
keys['ctrl+d'] = function() -- ctrl + d
	buffer:begin_undo_action()

	local lines = get_selected_or_focus_lines()
	local paired_lines = pair_line_numbers(lines)

	local lines_change_offset = 0
	for key, line in pairs(paired_lines) do
		if (type(line) == "table") then
			duplicate_line(line, lines_change_offset)
			lines_change_offset = lines_change_offset + (line['ending'] - line['beginning']) + 1
		else
			duplicate_line(line, lines_change_offset)
			lines_change_offset = lines_change_offset + 1
		end
	end
	buffer:end_undo_action()
end


-- highlight trailing whitespace
-- from: https://foicica.com/wiki/highlight-trailing-whitespace
local tw_indicator = _SCINTILLA.next_indic_number()
buffer.indic_style[tw_indicator] = buffer.INDIC_ROUNDBOX
buffer.indic_fore[tw_indicator] = 0x0000FF
events.connect(events.UPDATE_UI, function(updated)
	if updated ~= buffer.UPDATE_CONTENT then return end
	buffer.target_start = 0
	buffer.search_flags = buffer.FIND_REGEXP
	buffer.indicator_current = tw_indicator
	buffer:indicator_clear_range(0, buffer.length)
	while true do
	buffer.target_end = buffer.length
	if buffer:search_in_target('[ \t]+$') == -1 then break end
		buffer:indicator_fill_range(
		buffer.target_start, buffer.target_end - buffer.target_start)
		buffer.target_start = buffer.target_end
	end
end)


-- from https://github.com/rgieseke/textadept/blob/d184ca77570bdc72a97b9dee02ee8cea9844c9a6/modules/textadept/editing.lua#L208
function trim_whitespace(buffer)
	buffer:begin_undo_action()
	-- Strip trailing whitespace.
	for line = 0, buffer.line_count - 1 do
		local s, e = buffer:position_from_line(line), buffer.line_end_position[line]
		local i, byte = e - 1, buffer.char_at[e - 1]
		while i >= s and (byte == 9 or byte == 32) do
			i, byte = i - 1, buffer.char_at[i - 1]
		end
		if i < e - 1 then buffer:delete_range(i + 1, e - i - 1) end
	end
	-- Ensure ending newline.
	local e = buffer:position_from_line(buffer.line_count)
	if buffer.line_count == 1 or
			e > buffer:position_from_line(buffer.line_count - 1) then
		buffer:insert_text(e, '\n')
	end
	-- Convert non-consistent EOLs
	buffer:convert_eols(buffer.eol_mode)
	buffer:end_undo_action()
end


-- selects the line with the line_number given
function select_line(line_number)
	local line_beginning = buffer:position_from_line(line_number)
	local line_ending = buffer.line_end_position[line_number]
	buffer.add_selection(line_ending, line_beginning)
end

keys['ctrl+l'] = function() -- ctrl + l
	local lines = get_selected_or_focus_lines()

	for key, line in ipairs(lines) do
		select_line(line)
	end
end

-- close tab on middle click
-- patching source is required to work
if events.TAB_MIDDLE_CLICK then
	events.connect(events.TAB_MIDDLE_CLICK, function()
		io.close_buffer()
	end)
end

-- new tab on middle click on blank space
-- patching source is required to work
if events.TAB_MIDDLE_CLICK_BLANK then
	events.connect(events.TAB_MIDDLE_CLICK_BLANK, buffer.new)
end

-- new tab on double click on blank space
-- patching source is required to work
if events.TAB_DOUBLE_CLICK_BLANK then
	events.connect(events.TAB_DOUBLE_CLICK_BLANK, buffer.new)
end

-- shows total number of selected characters on statusbar
events.connect(events.UPDATE_UI, function()
    if buffer.UPDATE_SELECTION ~= 0 then
		local selcount = 0
		for i = 1, buffer.selections do
			selcount = selcount + (buffer.selection_n_end[i] - buffer.selection_n_start[i])
		end
		ui.statusbar_text = 'Sel: '..selcount
    end
end)


---- context menu
local lua_menu = {
	title = 'Extra',
	{'Copy current filename', function() buffer.copy_text(_G.buffer, _G.buffer.filename) end},
	{'Trim leading whitespace', function() trim_whitespace(_G.buffer) end},
	{'Set Filetype', keys['ctrl+L']},
}
local options_menu = textadept.menu.menubar[_L['Tools']]
options_menu[#options_menu + 1] = lua_menu
textadept.menu.context_menu[#textadept.menu.context_menu + 1] = lua_menu


-- various other language related mods
_M.mods = require 'mods'
_M.mods = require 'hotexit'
